package dk.dashboardFP.dfpLogger {
	import dk.dashboardFP.DashboardFP;
	import dk.dashboardFP.panels.introspectionPanel.IntrospectionNode;
	import dk.dashboardFP.panels.loggerPanel.InspectDCLogItem;
	import dk.dashboardFP.dfpLogger.logReceivers.ILogReceiver;

	import flash.display.DisplayObject;
	import flash.events.EventDispatcher;

	/**
	 * @author josef
	 */
	public class DFPLogger extends EventDispatcher {

		private static var _instance : DFPLogger;
		private static var _bCreatingInstance : Boolean = false;

		private var _o_oLogGroups : Object = {};
		private var _a_oLogReceivers : Array = [];

		public function DFPLogger() {
			if( !_bCreatingInstance ) throw new Error("Singleton and can only be accessed through DFPLogger.get_oInstance()");
			init();
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// init
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function init() : void {
			_getLogGroupById(-1).color = 0x999999;
			_getLogGroupById(-1).name = "Default";
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// log groups
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		/**
		 * Returns a log group from an id.
		 * 
		 * <p>Use this to retrive groups for setting of color, name etc.<br>For example : <code>getLogGroupById(3).name='Api Calls';</code></p>
		 * 
		 * @param groupId					An integer group id
		 * 
		 * @return LogGroup					A log group for the given id. If no group exists for the given id a new will be created and returned.
		 * 
		 * @see dk.dashboardFP.dfpLogger.LogGroup
		 */
		public static function getLogGroupById(groupId : int) : LogGroup {
			return DFPLogger.getInstance()._getLogGroupById(groupId);
		}

		/**
		 * Set name and color for a given group.
		 * 
		 * @param groupId			The id of the group that you wish to set.
		 * @param name				The name to apply.
		 * @param color				The color to apply. 
		 */
		public static function formatGroup(groupId : int, name : String, color : uint = 0) : void {
			var group : LogGroup = getLogGroupById(groupId);
			group.name = name;
			group.color = color;
		}

		private function _getLogGroupById(intGroup : int) : LogGroup {
			if (!_o_oLogGroups[intGroup]) {
				_o_oLogGroups[intGroup] = new LogGroup(intGroup);
			}
			return _o_oLogGroups[intGroup];
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// interface
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		/**
		 * The main log method.
		 * 
		 * <p>Use this method to log messages/object to the logger panel</p>
		 * 
		 * @param oMessage					The message that will be logged.
		 * @param object					An object that will be avalible for introspection via the logger panel.
		 * @param groupId					The id of the group to where the log-item should belong.
		 * @param level						The level of the log item. Choose from the statics in DFPLoggerLevels - INFO, DEBUG, WARN, ERROR.
		 * @param extraReceiverParms		If using custom made log receivers use this to pass extra custom parms.
		 * 
		 * @see #addLogReceiver()
		 * @see dk.dashboardFP.dfpLogger.DFPLoggerLevels 
		 */
		public static function log(message : Object, object : Object = null, groupId : int = -1, level : int = 0, extraReceiverParms : Object = null) : void {
			getInstance().doLog(message.toString(), groupId, object, level, extraReceiverParms);
		}

		/**
		 * Trace current execution stack.
		 * 
		 * <p>Use this to se the sequence of the method calls up to where the logStack is called</p>
		 * 
		 * @param groupId					The id of the group to where the log-item should belong.
		 * @param level						The level of the log item. Choose from the statics in DFPLoggerLevels - INFO, DEBUG, WARN, ERROR.
		 */
		public static function logStack(intGroup : int = -1, intLevel : int = 0) : void {
			
			var e : Error = new Error();
			var sLogString : String = "";
			var a_sLogSegments : Array = e.getStackTrace().split("at ");
			
			a_sLogSegments.splice(0, 2);
			
			var sLastClassName : String = "";
			
			var rootNode : IntrospectionNode = new IntrospectionNode(0, null);
			rootNode.sValue = "Stack";
			
			var lastClassNode : IntrospectionNode = new IntrospectionNode(0, null);
			
			for each (var s : String in a_sLogSegments) {
				
				var sClassName : String 
				if (s.indexOf("::") != -1) {
					sClassName = s.slice(s.indexOf("::") + 2, s.indexOf("/"));
				} else {
					sClassName = s.slice(0, s.indexOf("/"));
				}
				
				if (sClassName != lastClassNode.sValue) {
					var classNode : IntrospectionNode = new IntrospectionNode(1, null);
					classNode.sValue = sClassName;
					lastClassNode = classNode;
					rootNode.addSubNode(classNode);
				}
				
				var methodName : String = s.slice(s.indexOf("/") + 1);
				var methodNode : IntrospectionNode = new IntrospectionNode(3, null);
				
				if (methodName.indexOf("as:") != -1) {
					var sLineNumber : String;
					sLineNumber = s.slice(s.indexOf("as:") + 3, s.indexOf("]"));
					sLogString += "   Line: " + sLineNumber;
					methodName = (methodName.slice(0, methodName.indexOf("[")) + "  :  " + sLineNumber);
				}
				
				methodNode.sValue = methodName.split("\r").join("").split("\n").join("");
				
				lastClassNode.addSubNode(methodNode);
				/*
				 * 
				 */
			}
			
			var label : String = IntrospectionNode(rootNode.a_oSubNodes[0]).sValue + " :: " + IntrospectionNode(IntrospectionNode(rootNode.a_oSubNodes[0]).a_oSubNodes[0]).sValue;
			DFPLogger.inspect(label, rootNode, intGroup, "", false, intLevel);
			
			/*
				
			for each (var s : String in a_sLogSegments) {
				
			var sClassName : String = s.slice(s.indexOf("::") + 2, s.indexOf("/"));
			if (sClassName != sLastClassName) {
						
			if (sClassName != "EventDispatcher") {
			sLogString += sClassName + "\n";
			} else {
			sLogString += "<--- " + sClassName + " --->" + "\n";
			;
			}
			sLastClassName = sClassName;
			}
					
			if (sLastClassName != "EventDispatcher") {
			var sFunctionName : String = s.slice(s.indexOf("/") + 1, s.indexOf("()") + 2);
			sLogString += "       " + sFunctionName;
						
			if (s.indexOf("as:") != -1) {
			var sLineNumber : String;
			sLineNumber = s.slice(s.indexOf("as:") + 3, s.indexOf("]"));
			sLogString += "   Line: " + sLineNumber;
			}
			sLogString += "\n";
			}
			}
			 * 
			 */
				
			//:::::::
			// remove end line breaks
			sLogString = sLogString.slice(0, sLogString.lastIndexOf(")") + 1); 
				
			//:::::::
			// do log 
			//log(sLogString, null, intGroup, intLevel);
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// introspction interaface
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public static function inspect(message : String = "", object : Object = null, intGroup : int = -1, preSearch : String = "", autoOpen : Boolean = false, intLogLevel : int = 0) : void {
			_instance.doInspect(message, object, intGroup, preSearch, autoOpen, intLogLevel);
		}

		private function doInspect(message : String, object : Object, intGroup : int, preSearch : String, autoOpen : Boolean, intLogLevel : int) : void {
			
			//:::::::
			// escape log if group is hidden and bLogWhileHidden is set to false 
			if (!DFPLogger.getLogGroupById(intGroup).visible && !DFPLogger.getLogGroupById(intGroup).logWhileHidden ) return;
			
			var inspectLogItem : InspectLogItem = new InspectLogItem(message, intGroup, object, intLogLevel, preSearch, autoOpen);
		
			//:::::::
			// pass on 
			if (getLogGroupById(inspectLogItem.intGroup).visible) {
				passLogToReceivers(inspectLogItem);
			}
		
			dispatchEvent(new DFPLoggerEvent(DFPLoggerEvent.LOG, inspectLogItem));
		}

		/**
		 * Inspect the display chain decending from a given DisplayObject.
		 * 
		 * @param message					The message that will be logged.
		 * @param inspectionRoot			The DisplayObject that will be the base of the inspection.
		 * @param groupId					The id of the group to where the log-item should belong.
		 * @param level						The level of the log item. Choose from the statics in DFPLoggerLevels - INFO, DEBUG, WARN, ERROR.
		 * @param preSearch					A predefined search string that will be set when opening the inspection.
		 * @param autoOpen					Wether the inspeciton should be opned automaticly.				
		 *  
		 */
		public static function inspectDisplayChain(message : String = "", inspectionRoot : DisplayObject = null, intGroup : int = -1, preSearch : String = "", autoOpen : Boolean = false, logLevel : int = 0) : void {
			_instance.doInspectDC(inspectionRoot, message, intGroup, preSearch, autoOpen, logLevel);
		}

		private function doInspectDC(inspectionRoot : DisplayObject, message : String = "", intGroup : int = -1, preSearch : String = "", autoOpen : Boolean = false, logLevel : int = 0) : void {

			//:::::::
			// escape log if group is hidden and bLogWhileHidden is set to false 
			if (!DFPLogger.getLogGroupById(intGroup).visible && !DFPLogger.getLogGroupById(intGroup).logWhileHidden ) return;
			
			var inspectLogItem : InspectDCLogItem = new InspectDCLogItem(message, intGroup, inspectionRoot, logLevel, preSearch, autoOpen);
			dispatchEvent(new DFPLoggerEvent(DFPLoggerEvent.LOG, inspectLogItem));
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// log dispatcher
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function doLog(sMessage : String, intGroup : int, oData : Object, intLevel : int, oExtraReceiverParms : Object) : void {
			
			//:::::::
			// escape log if group is hidden and bLogWhileHidden is set to false 
			if (!DFPLogger.getLogGroupById(intGroup).visible && !DFPLogger.getLogGroupById(intGroup).logWhileHidden ) return;
			
			// escape if log level is less than groups setting 
			if (DFPLogger.getLogGroupById(intGroup).level > intLevel && !DFPLogger.getLogGroupById(intGroup).logWhileHidden) return;
			
			var oLogItem : DFPLogItem = new DFPLogItem(sMessage, intGroup, oData, intLevel, oExtraReceiverParms);
			
			//:::::::
			// pass on 
			if (getLogGroupById(oLogItem.intGroup).visible) {
				passLogToReceivers(oLogItem);
			}

			dispatchEvent(new DFPLoggerEvent(DFPLoggerEvent.LOG, oLogItem));
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// recievers
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		//:::::::
		// static interface
		
		/**
		 * Adds a log receivet to the logger.
		 * 
		 * <p>This registers external loggers like ThunderBolt, MonsterDebugger etc. to DFPLogger and all logs made to DFPLogger will be passed on to these as well as apparing in the internal panel</p>
		 * 
		 * @param logReceiver					A logger bridge implementing the ILogReceiver interface.
		 * 
		 * @see dk.dashboardFP.dfpLogger.logReceivers.ILogReceiver
		 */ 
		public static function addLogReceiver(logReceiver : ILogReceiver) : void {
			getInstance().doAddLogReceiver(logReceiver);
		}

		private function doAddLogReceiver(logReceiver : ILogReceiver) : void {
			_a_oLogReceivers.push(logReceiver);
		}

		private function passLogToReceivers(logItem : DFPLogItem) : void {
			for each (var oLogReceiver : ILogReceiver in _a_oLogReceivers) {
				oLogReceiver.log(logItem.sMessage, logItem.oData, logItem.intLevel, logItem.oExtraReceiverParms);
			}
		}		

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// singleton enforcer
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public static function getInstance() : DFPLogger {
			if (!_instance) {
				_bCreatingInstance = true;
				_instance = new DFPLogger();
				_bCreatingInstance = false;
			}
			return _instance;
		}
	}
}
